// Author: Zhang, Yichao
// Date: 2009-12-3
// For G-Crossbit

#ifndef VBLOCKANALYZER_H
#define VBLOCKANALYZER_H

#include "common.h"
#include "VBlock.h"
#include "VInst.h"
#include "VBlockBuilder.h"
#include "Interpreter.h"
#include "map"
#include "vector"
#include "queue"
#include "stack"
#include "list"
#include "set"
#include "algorithm"
#include "fstream"

#include "g_crossbit/Record.h"

using namespace std;
using namespace g_crossbit;

namespace crossbit
{
	class VBlockAnalyzer
	{
		typedef map<XTMemAddr, XTInt64>::iterator block_count_it;
		typedef map<XTInt64, XTInt64>::iterator branch_count_it;
		typedef map<XTMemAddr, VBlock *>::iterator vblock_it;

		typedef struct struct_var_info
		{
			XTRegNum reg;		// base reg
			XTInt32 off;		// offset

			struct_var_info()
				:reg(0), off(0)
			{}

			struct_var_info(XTRegNum r, XTInt32 f)
				:reg(r), off(f)
			{}

			// TODO: assume reg always equal to 5
			bool operator<(const struct_var_info &a) const
			{
				return (this->off < a.off);
			}

			bool operator==(const struct_var_info &a) const
			{
				if(this->reg == a.reg && this->off == a.off)
					return true;
				else
					return false;
			}
		}VAR_INFO;

		typedef struct struct_loop_info
		{
			VAR_INFO index;
			VAR_INFO bound;
			XTMemAddr entry;	// entry of the loop body(VBlock)
			int range;

			struct_loop_info()
			{
				entry = 0;
			}

			struct_loop_info(VAR_INFO i, VAR_INFO b, XTMemAddr t, XTInt32 r)
				:index(i), bound(b), entry(t), range(r)
			{}

			bool operator<(const struct_loop_info &a) const
			{
				return (this->entry < a.entry);
			}

			bool operator==(const struct_loop_info &a) const
			{
				return (this->index == a.index && this->bound == a.bound && this->entry == a.entry && this->entry == a.entry);
			}
		}LOOP_INFO;

		typedef struct struct_ld_info
		{
			Operand dst;				// destination
			vector<VAR_INFO> src;		// list of source addr
			XTInt32 imm;				// offset in the source inst
		}LD_INFO;

		typedef struct struct_st_info
		{
			Operand src;				// source Operand
			vector<VAR_INFO> dst;		// list of destination 
			XTInt32 imm;				// offset in the source inst
		}ST_INFO;

		typedef struct struct_trace_node
		{
			XTInt32 num;			// VInst number
			Operand opr;

			struct_trace_node(XTInt32 n, Operand o)
				:num(n), opr(o)
			{}
		}TRACE_NODE;

		typedef struct struct_matrix_info
		{
			VAR_INFO base_addr;
			VAR_INFO width;
			VAR_INFO height;

			struct_matrix_info(VAR_INFO b, VAR_INFO w, VAR_INFO h)
				:base_addr(b), width(w), height(h)
			{}
		}MATRIX_INFO;

		// TODO: temp use
		typedef struct struct_index_range
		{
			VAR_INFO index;
			XTInt32 range;

			struct_index_range(VAR_INFO i, XTInt32 r)
				:index(i), range(r)
			{}

			bool operator<(const struct_index_range &a) const
			{
				return this->range < a.range;
			}
		}INDEX_RANGE;

		public:
		VBlockAnalyzer();
		VBlockAnalyzer(XTMemAddr ss, XTMemAddr se);

		~VBlockAnalyzer();

		static void SetStreamArea( XTMemAddr start, XTMemAddr end )
		{
			stream_start = start;
			stream_end = end;
			// XXX
			if( stream_start == 0x8048254 && stream_end == 0x80483e7 )
				is_fft2d = true;
			else
				is_fft2d = false;
		}
		static void PrintStreamArea()
		{
			printf("Stream_start = 0x%x, Stream_end = 0x%x\n", stream_start, stream_end);
		}
		static bool IsInStreamArea( XTMemAddr spc )
		{
			return ( spc >= stream_start && spc <= stream_end );
		}

		// Add Vblock Count
		void AddVBlockCount(XTMemAddr entry);

		// Find speedup area(vblock), just find loop now.
		void FindSpeedUpArea(VBlockBuilder *builder, Interpreter *interpreter);

		// Analyze the def-use information and dump it out
		// Unused, just keep it for back-up
		void AnalyzeVBlockDataFlow(XTMemAddr entry);

		// Analyze DataFlow of all vblocks
		void AnalyzeDataFlows();

		// find loop
		void FindLoop(XTMemAddr entry, XTMemAddr exit);

		// Dump the information to file
		void DumpEntries();
		void DumpRecords();
		void DumpVBlocksText();
		void DumpVBlocksByte();

		// Interfaces for other component, added by zhangyichao, 2010-4-12
		void LoopBoundAnalyzer(VBlock *vb, RegNum &reg, XTInt32 &off);

		private:
		XTInt32 __LookUpLatestDef(XTMemAddr entry, int &num, TRACE_NODE node, Operand &res, DataType &dt);

		void __GetDefUseChain(XTMemAddr entry);
		void __GetLDInfo(XTMemAddr entry);
		void __GetLoopInfo(XTMemAddr entry);
		void __GetDataInfo(XTMemAddr entry);
		void __ProfileCollector();
		void __VblockFilter(XTMemAddr entry);

		// added on 2010-4-12
		VAR_INFO __GetLoopBoundGeneral(XTMemAddr entry);
		void __GetLDInfoGeneral(XTMemAddr entry);
		bool __isSameOpr(const Operand lopr, const Operand ropr);

		map<XTMemAddr, XTInt64> block_count;

		set<XTMemAddr> area_entries;
		set<XTMemAddr> analyzed_vblocks;
		map<XTMemAddr, XTMemAddr> loops;	// <entry, exit>
		map<XTMemAddr, VBlock *> area_vblocks;
		map<XTMemAddr, VBlock *> hot_vblocks;
		vector< vector < Operand > > def;	// def-chain for each VInst
		vector< vector < Operand > > use;	// use-chain for each VInst
		map<XTInt32, LD_INFO> ld_info;		// map<VInst No., LD_INFO>
		map<XTInt32, ST_INFO> st_info;		// map<VInst No., ST_INFO>
		map<Operand, XTInt32>  def_table;	// store the latest def location of Vreg

		vector<LOOP_INFO> loop_info;
		vector<VAR_INFO> matrix_base;
		vector<VAR_INFO> temp_var;
		vector<VAR_INFO> width;
		vector<VAR_INFO> index;
		vector<VAR_INFO> func_args;

		vector<Record> records;

		XTMemAddr enter, exit;

		// find by NestedLoopDetector now.
		static XTMemAddr stream_start;
		static XTMemAddr stream_end;
		static bool is_fft2d;

	};
}

#endif
